<template>
  <!-- 自学模式 -->
  <div class="sbodyd">
    <div class="mainboxgj">
      <!-- 分2 -->
      <div class="mainboxleft">
        <div class="mainlefttopbox">
          <div class="mainsera">
            <div class="xbannername" @click="goback">
              <icon-left /> 返回图谱列表
            </div>
            <div
              class="startbox"
              @click="startfn"
              v-if="!startshow && addbsshowlx"
              >开始练习</div
            >
            <div
              class="startbox"
              @click="startfns"
              v-if="startshow && addbsshowlx"
              >退出练习</div
            >
            <div
              class="startbox"
              @click="allclear"
              v-if="startshow && addbsshowlx"
              >清空作答</div
            >
            <div class="startboxs" v-if="startshow && addbsshowlx"
              >显示答案
              <a-switch v-model="state.showText" @change="toggleTextButton"
            /></div>
          </div>

          <div class="mainsers" v-if="!addbsshowlx">
            <a-select
              v-model="markingPointsIdxz"
              allow-search
              allow-clear
              :options="optionsselect"
              :style="{ width: '320px' }"
              placeholder="请输入"
              @search="handleSearch"
              :filter-option="false"
              :show-extra-options="false"
              :field-names="{ value: 'markingPointsId', label: 'text' }"
              @change="changeselect"
            />
            <!-- <a-input-search
              @input="inputser"
              :style="{ width: '269px' }"
              allow-clear
              placeholder="请搜索相关点位内容"
            >
              <template #button-icon>
                <icon-search class="st" />
              </template>
            </a-input-search> -->
          </div>
          <div class="maintoptitle">{{ partsName }}</div>
          <div class="maintopright">
            <div class="czmainitem tool" @click="zoomIn">
              <img src="@/assets/images/fd.png" alt="" class="normal" />
              <img src="@/assets/images/fdac.png" alt="" class="active" />
            </div>
            <div class="czmainitem tool" @click="zoomOut">
              <img src="@/assets/images/sx.png" alt="" class="normal" />
              <img src="@/assets/images/sxac.png" alt="" class="active" />
            </div>
            <div class="czmainitem tool" @click="tdflag = true">
              <img src="@/assets/images/td.png" alt="" class="normal" />
              <img src="@/assets/images/tdac.png" alt="" class="active" />
            </div>
          </div>
        </div>
        <div class="box" ref="box" id="canvasContainer">
          <canvas
            id="myCanvas"
            :width="hbwidth"
            :height="hbheight"
            :style="{
              cursor: myStyle,
              backgroundColor: atlasTypeName == '插画' ? '#ffff' : 'black',
            }"
          >
          </canvas>
          <!--           @mousedown="currentMode == 'drawing' ? startDrawing : null"
            @mouseup="currentMode == 'drawing' ? stopDrawing : null"
            @mousemove="currentMode == 'drawing' ? draws : null" -->
          <canvas
            v-show="isCanvasVisible"
            id="overlayCanvas"
            :width="hbwidth"
            :height="hbheight"
            ref="overlayCanvas"
            @touchstart="startDrawing"
            @touchend="stopDrawing"
            @touchmove="draws"
            @mousedown="startDrawing"
            @mouseup="stopDrawing"
            @mousemove="draws"
            @click="showTextInput"
            :style="{ cursor: currentCursorStyle }"
          >
          </canvas>
          <div class="overlayCanvastool" v-show="isCanvasVisible">
            <div
              class="overlayCanvastooitem"
              :class="currentMode == 'drawing' ? 'overlayCanvastooitems' : null"
              @click="enableDrawingMode"
              >✎</div
            >
            <div
              class="overlayCanvastooitem"
              :class="currentMode == 'text' ? 'overlayCanvastooitems' : null"
              @click="enableTextMode"
              >T</div
            >
            <div
              class="overlayCanvastooitem"
              :class="
                currentMode == 'download' ? 'overlayCanvastooitems' : null
              "
              @click="downloadImage"
              >☑</div
            >
            <div class="overlayCanvastooitem" @click="undo">撤销</div>
            <div class="overlayCanvastooitem" @click="redo">还原</div>
            <div class="overlayCanvastooitem" @click="toggleEraser">橡皮</div>

            <div class="overlayCanvastooitem" @click="clearOverlay">删除</div>
            <div>
              <input id="color" type="color" v-model="brushColor" />

              <input
                id="size"
                type="range"
                min="1"
                max="10"
                v-model="brushSize"
              />
            </div>
          </div>
          <div
            v-if="showInput"
            :style="inputStyle"
            class="text-input-container"
          >
            <input
              v-model="customText"
              @keydown.enter="addTextMarkerOnEnter"
              class="text-input"
              placeholder="输入要添加的文字"
              @focus="isInputActive = true"
              @blur="handleInputBlur"
            />
          </div>
        </div>
        <div class="lbox" ref="gosroll" @scroll="getScrollPosition">
          <div
            v-for="(item, index) in imgsrc"
            :key="index"
            class="lboxitem"
            @click="tagdcom(item.medicineAtlasImgId, '')"
          >
            <img :src="`${base + item.imgUrl}`" />
            <div class="msx" v-if="item.medicineAtlasImgId == dcomid"></div>
          </div>
        </div>
      </div>
      <!-- 分右边 -->

      <div class="bocd">
        <div class="gnbocdtop">
          <!-- <div class="xugn" v-if="imggroup.length > 0">
            <div
              class="jpan"
              v-for="item in imggroup"
              :class="item.typeName == imggroupname ? 'jpanac' : ''"
              :key="item.typeName"
              @click="changeimggroupfn(item)"
            >
              <div class="jpanitem" :title="item.typeName">
                {{ item.typeName }}</div
              >
            </div>
          </div>
          <div class="xugn">
            <div
              class="jpan"
              :key="item.classificationName"
              v-for="item in imgclassdata"
              :class="
                item.classificationName == classificationName ? 'jpanac' : ''
              "
              @click="changeimgclassfn(item)"
            >
              <div class="jpanitem" :title="item.classificationName">
                {{ item.classificationName }}
              </div>
            </div>
          </div> -->
          <div class="xugn">
            <div class="xugname">解剖面：</div>
            <a-select
              @change="changeimgclassfn"
              v-model="classificationName"
              :options="imgclassdata"
              :field-names="fieldNames"
              :style="{ width: '250px' }"
              placeholder="请选择解剖面"
            >
              <a-option @click="changecsd()">全部</a-option>
            </a-select>
          </div>

          <div class="xugn" v-if="imggroup.length > 0">
            <div class="xugname">图像组：</div>
            <a-select
              @change="changeimggroupfn"
              v-model="imggroupname"
              :options="imggroup"
              :field-names="fieldNamess"
              :style="{ width: '250px' }"
              placeholder="请选择图像组"
            >
              <a-option>全部</a-option>
            </a-select>
          </div>
        </div>
        <div class="gnbocd">
          <!-- <div class="gnbocdname" style="cursor: pointer" @click="jubu"
            >局部标签</div
          > -->
          <div class="gnbocdname">解剖部位</div>
          <div class="gnboxmain">
            <!-- <a-button type="primary" @click="reset">重置</a-button> -->
            <!-- <div class="gnboxitem jgdovx">
              双指缩放
              <a-switch v-model="isZooming" v-if="optionsdata.length > 0" />
            </div> -->
            <!-- <div class="gnboxitem jgdovx">
              单指滑动
              <a-switch v-model="oneshow" v-if="optionsdata.length > 0" />
            </div> -->
            <!-- <div class="gnboxitem jgdovx">
              添加批注
              <a-switch
                v-model="pzshow"
                @change="pzfn()"
                v-if="optionsdata.length > 0"
              />
            </div>
            <div class="gnboxitem jgdovx">
              局部标签
              <a-switch
                v-model="jubushow"
                @change="jubu()"
                v-if="optionsdata.length > 0"
              />
            </div> -->
            <div class="gnboxitem jgdovx" v-if="!startshow">
              教学模式
              <a-switch
                v-model="jubushow"
                @change="jubu()"
                v-if="optionsdata.length > 0"
              />
            </div>
            <div class="gnboxitem jgdovx" v-if="!startshow">
              大头针模式
              <a-switch
                v-model="tackshow"
                @change="tackfn()"
                v-if="optionsdata.length > 0"
              />
            </div>
            <div class="gnboxitem jgdovx">
              所有
              <a-switch
                v-model="allflag"
                @change="changecsd()"
                v-if="optionsdata.length > 0"
              />
            </div>
            <div v-for="(item, index) in anatomicStructure" :key="index">
              <div ref="cs" class="jgdovx gnboxitem">
                {{ item.anatomicStructureName }}
                <a-switch v-model="item.flag" @change="changecs(item)" />
              </div>
            </div>
            <!-- <div v-for="(item, index) in jiegoudata" :key="index">
              <div ref="cs" class="jgdovx gnboxitem" v-if="flagfn(item)">
                {{ item.anatomicStructureName }}
                <a-switch
                  v-model="JGkgdata[item.anatomicStructureId]"
                  @change="changecs(item.anatomicStructureId, item.bsid)"
                />
              </div>
            </div> -->
          </div>
        </div>
        <div class="gnbottom">
          <div class="tool" @click="tagdcoms('-1')">
            <img src="@/assets/images/letac.png" alt="" class="normal" />
            <img src="@/assets/images/let.png" alt="" class="active" />
          </div>
          <div class="mentox"
            >影像页数:<span>{{ findindex() + 1 }}/{{ imgsrc.length }}</span>
          </div>
          <div class="tool" @click="tagdcoms('1')">
            <img src="@/assets/images/rght.png" alt="" class="normal" />
            <img src="@/assets/images/rghtac.png" alt="" class="active" />
          </div>
        </div>
        <!-- <div>
          <a-card :style="{ width: '335px' }" title="功能菜单">
            <template #extra>
              <a-link></a-link>
            </template>

            <a-button type="primary" @click="zoomIn">放大</a-button>
            <a-button type="primary" @click="zoomOut">缩小</a-button>
            <a-button type="primary" @click="toggleLinesAndText"
              >显示旗标</a-button
            >
            <a-button type="primary" @click="flipHorizontald"
              >水平翻转</a-button
            >
            <a-button type="primary" @click="flipVerticald">垂直翻转</a-button>
            <a-button type="primary" @click="rotate90">旋转90</a-button>

            <a-button type="primary" @click="rotateMinus90">旋转-90</a-button>
           
          </a-card>
        </div> -->
        <!-- <div>
          <a-card :style="{ width: '340px' }" title="解剖部位">
            <template #extra>
              <a-link></a-link>
            </template>
            <div>
              <div v-if="flag">
                所有
                <a-switch v-model="allflag" @change="changecsd()" />
              </div>
              <div v-for="item in jiegoudata" :key="item">
                <div ref="cs" class="jgdovx" v-if="flagfn(item)">
                  {{ item.anatomicStructureName }}
                  <a-switch
                    v-model="JGkgdata[item.anatomicStructureId]"
                    @change="changecs(item.anatomicStructureId, item.bsid)"
                  />
                </div>
              </div>
            </div>
          </a-card>
        </div> -->
      </div>
    </div>

    <!-- 知识点补充div -->
    <div class="zsbox" v-if="visible">
      <div class="zstop"
        >知识点补充
        <img
          src="@/assets/images/close.png"
          alt=""
          class="zsclose"
          @click="handleCancel"
        />
      </div>
      <div class="zsbot">
        <div class="zsdkz">
          {{ bwstuddata.system }}> {{ bwstuddata.partsName }}>
          {{ bwstuddata.anatomicStructureName }}> {{ bwstuddata.pointName }}

          <div v-if="bwstuddata.pointContent != ''">
            <div class="zsdkz" v-html="bwstuddata.pointContent"> </div
          ></div>
          <div v-else>
            <div class="zsdkz">暂无知识点补充!</div>
          </div>
        </div></div
      >
    </div>

    <!-- 加载页 -->
    <div class="loader-container" v-if="loding">
      <div class="loader"></div>
      <p class="pd">加载中</p>
    </div>
    <!-- 文字气泡 -->
    <div
      class="speech-bubble"
      :style="inputStyles"
      v-if="state.hoveredMarker && (jubushow || tackshow)"
    >
      {{ state.hoveredMarker?.text }}
    </div>

    <!-- 弹出填写试题答案的选择框 -->
    <a-modal
      v-model:visible="visiblest"
      @cancel="handleCancelst"
      :on-before-ok="handleBeforeOk"
      unmountOnClose
    >
      <template #title> 填写 </template>
      <div>
        <a-form :model="formModel">
          <a-form-item field="userText" label="答案">
            <a-input
              v-model="formModel.userText"
              placeholder="请输入答案"
              allow-clear
            />
          </a-form-item>
        </a-form>
      </div>
    </a-modal>
  </div>
</template>

<script lang="ts" setup>
  // @ts - nocheck
  import { defineProps, defineEmits, CSSProperties } from 'vue';
  import { Message, Modal } from '@arco-design/web-vue';
  import { getPoint, getPointnew, getxqPoint, jpList } from '@/api/tplist';
  import {
    defineComponent,
    reactive,
    ref,
    nextTick,
    onMounted,
    onBeforeUnmount,
    SetupContext,
  } from 'vue';

  interface Marker {
    labels: boolean;
    x: number;
    y: number;
    color: string;
    text?: string;
    textX: any;
    textY: any;
    markingPointsId: any;
  }
  const loding = ref(false);
  const props = defineProps<{
    partsName: string;
    imgsrc: [{ medicineAtlasImgId: 1; imgUrl: '' }];
    medicineAtlasId: string;
    imgclassdata: string[];
    imggroup: string[];
    addbsshowlx: boolean;
    atlasTypeName: string;
  }>();
  const emit = defineEmits<{
    (event: 'goback'): void;
    (event: 'customEvent', payload: any): void; // 定义自定义事件
  }>();
  // 定义图谱类型
  const atlasTypeName = ref('');
  atlasTypeName.value = props.atlasTypeName;
  console.log('当前图谱类型', atlasTypeName.value, props);

  // 练习兼容性代码s
  // 开始练习
  const toggleTextButton = () => {
    // state.showText = !state.showText;
    console.log('当前值', state.showText);

    draw('');
  };
  const startshow = ref(false);
  // 练习标识
  const addbsshowlx = ref(false);
  addbsshowlx.value = props.addbsshowlx;
  const allclear = () => {
    // 清空作答
    // 查询标记点为
    getPointnew({
      medicineAtlasId: medicineAtlasId.value,
      pageNum: 1,
      pageSize: 800,
    }).then((res: any) => {
      if (res.code == 200) {
        state.markers = [];
        saveMarkers();
        console.log('查询解刨点测试', res.data);
        const markersMap = new Map(
          state.markers.map((marker: any) => [
            marker.medicineAtlasImgId,
            marker,
          ])
        );

        for (const items of res.data) {
          try {
            // 输出调试信息
            // console.log('Processing item:', items);
            // console.log(
            //   'Extracted medicineAtlasImgId:',
            //   items.medicineAtlasImgId
            // );

            if (items.medicineAtlasImgId != null) {
              const existingMarker = markersMap.get(items.medicineAtlasImgId);
              const markingPointsObjects = JSON.parse(items.markingPoints);
              markingPointsObjects.medicineAtlasImgId =
                items.medicineAtlasImgId;

              markingPointsObjects.bsdata[0].markingPointsId =
                items.markingPointsId;
              markingPointsObjects.bsdata[0].flag = true;
              markingPointsObjects.bsdata[0].labels = true;
              markingPointsObjects.bsdata[0].tack = false;
              markingPointsObjects.bsdata[0].anatomicStructureName =
                items.anatomicStructureName;
              markingPointsObjects.bsdata[0].anatomicStructureId =
                items.anatomicStructureId;

              if (existingMarker) {
                existingMarker.bsdata.push(...markingPointsObjects.bsdata);
              } else {
                markersMap.set(items.medicineAtlasImgId, markingPointsObjects);
              }
            }
          } catch (error) {}
        }

        // 更新 state.markers
        state.markers = Array.from(markersMap.values());

        // 确保数据不为空
        if (state.markers.length === 0) {
          console.error('Final markers array is empty. Check input data.');
        } else {
          console.log('Final markers:', state.markers);
        }

        // console.log('打印获取的点位数据', state.markers);

        draw('');

        loding.value = false;
      }
    });
  };

  const startfn = () => {
    state.showText = false;
    startshow.value = true;
    draw('');
  };
  // 退出练习
  const startfns = () => {
    state.showText = true;
    startshow.value = false;
    draw('');
  };
  // 弹出
  const visiblest = ref(false);
  // 提交答案验证
  const handleBeforeOk = (done: any) => {
    console.log('查询点位验证', state.currentMarker, state.markers);

    state.markers.forEach((item: any) => {
      if (!item.medicineAtlasImgId) {
        return;
      }
      const itemsArray = item.medicineAtlasImgId.split(',').map(Number);
      if (itemsArray.includes(dcomid.value)) {
        item.bsdata.forEach((marker: any) => {
          if (state.currentMarker == marker) {
            marker.userText = formModel.value.userText;
            draw('');
            console.log('判断到当前相等修改然后保存缓存并且要执行draw', marker);
          }
        });
      }
    });

    visiblest.value = false;
    done(false);
    formModel.value.userText = '';
  };
  // 填写
  const handleCancelst = () => {
    visiblest.value = false;
    formModel.value.userText = '';
  };
  const formModel = ref({
    userText: '',
  });
  //练习兼容代码完成
  // 定义搜索成绩展示
  const markingPointsIdxz = ref();

  const optionsselect = ref<any[]>([]);
  const handleSearch = (e: any) => {
    optionsselect.value = [];
    // console.log('判断输入和框的', e);
    // 输入框输入的内容 是额
    state.markers.forEach((item: any) => {
      item.bsdata.forEach((items: any) => {
        if (items.text.includes(e)) {
          // state.hoveredMarker = items;
          optionsselect.value.push(items);
          // console.log('模糊搜索后的展示', items.text);
        }
      });
    });
  };
  // 选择搜索结果后显示
  const changeselect = (e: any) => {
    // e 是markingPointsId  根据id 查找符合的 然后进行重新回话
    console.log('当前选择是数据', e);

    state.markers.forEach((item: any) => {
      item.bsdata.forEach((items: any) => {
        if (items.markingPointsId == e) {
          if (!item.medicineAtlasImgId) {
            return;
          }
          let firstPart = item.medicineAtlasImgId.split(',')[0];
          state.hoveredMarker = items;

          tagdcom(Number(firstPart), '搜索');
        }
      });
    });
  };
  // 搜索完成

  const visible = ref(false);

  const handleClick = () => {
    if (!addbsshowlx.value) {
      visible.value = true;
    } else {
      visiblest.value = true;
    }
  };
  const handleOk = () => {
    visible.value = false;
  };

  const handleCancel = () => {
    visible.value = false;
  };

  const imgsrc = ref<{ medicineAtlasImgId: number; imgUrl: string }[]>(
    props.imgsrc || []
  );

  // 重复的
  const imgsrcs = ref(props.imgsrc);
  const imggroup = ref(props.imggroup);
  const imgclassdata = ref(props.imgclassdata);
  const partsName = ref(props.partsName);
  // console.log('partsNamepartsName', partsName.value);

  const base = ref((window as any).config.VUE_APP_IMG_API);
  // console.log('Win', imgclassdata.value, imggroup.value);
  const medicineAtlasId = ref(props.medicineAtlasId);
  // console.log('接收到', imgsrc.value, base.value);
  const state = reactive({
    labels: null,
    showText: true,
    canBiaoZhu: false,
    pointColor: '#65FF00',
    pointSize: 7,
    // 手机端 0.4 然后设置间距搞点吧
    imgScale: 1,
    zoomStep: 0.1,
    markers: [] as { dcomid: number; bsdata: Marker[] }[],
    canvas: null as unknown as HTMLCanvasElement,
    ctx: null as CanvasRenderingContext2D | null,
    img: new Image(),
    // imgWidth: 1300,
    // imgHeight: 500,
    imgWidth: 480,
    imgHeight: 606,
    imgPosition: { x: 0, y: 0 },
    currentText: '',
    currentMarker: {},
    showLinesAndText: true,
    isDragging: false,
    startDragX: 0,
    startDragY: 0,
    imgOffsetX: 0,
    imgOffsetY: 0,
    isSpacePressed: false,
    hoveredMarker: null as Marker | null,
    editingMarker: false,
    editPanelX: 0,
    editPanelY: 0,
    addflag: false,
    flipHorizontal: false,
    flipVertical: false,
    rotation: 0, // 0, 90, -90
  });
  const markingPointsId = ref('');
  const dcomid = ref(1);
  dcomid.value = Number(localStorage.getItem('dcomid')) || 1;

  const goback = () => {
    emit('goback');
  };
  const flipHorizontald = () => {
    state.flipHorizontal = !state.flipHorizontal;
    draw('');
  };

  const flipVerticald = () => {
    state.flipVertical = !state.flipVertical;
    draw('');
  };

  const rotate90 = () => {
    state.rotation = (state.rotation + 90) % 360;
    draw('');
  };

  const rotateMinus90 = () => {
    state.rotation = (state.rotation - 90 + 360) % 360;
    draw('');
  };
  const checkHover = (x: number, y: number) => {
    // console.log('触发了点击判断');
    state.hoveredMarker = null;
    myStyle.value = 'inherit';
    // 从状态中解构出相关的属性
    const { markers, pointSize, imgScale } = state;
    // 合并 bsdata
    const mergedBsdata = markers.filter((item: any) =>
      item.medicineAtlasImgId.includes(dcomid.value)
    );
    const targetElement = { bsdata: [] as Marker[] };
    targetElement.bsdata = mergedBsdata.flatMap((item) => item.bsdata);

    if (targetElement) {
      // 找到目标标记元素中的标记
      for (let marker of targetElement.bsdata) {
        // 获取画布和上下文
        const canvas = state.canvas!;
        const ctx = state.ctx!;

        // 计算缩放后的图像尺寸
        const scaledWidth = state.imgWidth * imgScale;
        const scaledHeight = state.imgHeight * imgScale;

        // 计算图像在画布上的偏移量
        const xOffset = (canvas.width - scaledWidth) / 2 + state.imgOffsetX;
        const yOffset = (canvas.height - scaledHeight) / 2 + state.imgOffsetY;

        // 计算标记的位置
        const markerX = marker.x * imgScale + xOffset;
        const markerY = marker.y * imgScale + yOffset;

        // 检查是否悬停在标记点上
        // 如果是大头针模式增加判断范围
        let imgScalebs = 1;
        if (tackshow.value) {
          imgScalebs = 4;
        }
        if (
          Math.hypot(x - markerX, y - markerY) <
          (pointSize / 2) * imgScale * imgScalebs
        ) {
          // console.log('悬停在标记');

          state.hoveredMarker = marker;

          myStyle.value = 'pointer';
          return true;
        }

        // 检查是否悬停在文本上
        const text = marker.text || '';
        ctx.font = `14px Arial`; // 根据实际使用的字体进行设置
        const textMetrics = ctx.measureText(text);
        const textWidth = textMetrics.width + 40; //适当加大范围

        const textX = marker.textX;
        const textY = marker.textY;
        const textHeight = 16 * imgScale;

        // 计算文本的边界框
        const textBoundingBox = {
          left: textX - textWidth,
          right: textX + textWidth,
          top: textY - textHeight,
          bottom: textY,
        };

        if (
          x >= textBoundingBox.left &&
          x <= textBoundingBox.right &&
          y >= textBoundingBox.top &&
          y <= textBoundingBox.bottom
        ) {
          myStyle.value = 'pointer';
          state.hoveredMarker = marker;
          return true;
        }
      }
    }

    // 没有悬停在任何标记、线段或文本上
    state.hoveredMarker = null;
    return false;
  };

  const handleMouseMove = (e: MouseEvent | TouchEvent) => {
    // console.log('触摸事件');

    // 判断是否为触摸事件
    const isTouchEvent = (e: MouseEvent | TouchEvent): e is TouchEvent =>
      'touches' in e;

    let canvasX: number, canvasY: number;

    if (isTouchEvent(e)) {
      // 获取触摸事件的坐标
      const touch = e.touches[0];
      const rect = state.canvas!.getBoundingClientRect();
      canvasX = touch.clientX - rect.left;
      canvasY = touch.clientY - rect.top;
    } else {
      // 获取鼠标事件的坐标
      canvasX = e.offsetX;
      canvasY = e.offsetY;
    }

    if (state.isDragging) {
      dragMove(e);
    } else {
      checkHover(canvasX, canvasY);
      // 判断是否是自学模式

      if ((jubushow.value || tackshow.value) && state.hoveredMarker) {
        inputStyles.value = {
          left: `${canvasX - 30}px`, // 可以考虑做一些微调
          top: `${canvasY + 5}px`, // 根据需要微调
          // 使输入框的光标对齐到点击位置
        };
        return;
      }
      if (state.hoveredMarker) {
        draw('搜索');
      } else {
        draw(' ');
      }
    }
  };
  const inputStyles = ref<CSSProperties>({
    left: '',
    top: '',
    // 'absolute' 是一个有效的 position 类型
  });
  const handleCanvasClick = (e: MouseEvent | TouchEvent) => {
    // console.log('当前是查询点击');

    let canvasX: number, canvasY: number;

    if ('touches' in e) {
      // 触摸事件
      const touch = e.touches[0];
      const rect = state.canvas!.getBoundingClientRect();
      canvasX = touch.clientX - rect.left;
      canvasY = touch.clientY - rect.top;
      checkHover(canvasX, canvasY);
      // 只有在需要阻止默认行为时调用
      e.preventDefault();
    } else {
      // 鼠标事件
      canvasX = e.offsetX;
      canvasY = e.offsetY;
    }

    // 判断有没有开启自学模式
    if (jubushow.value && state.hoveredMarker) {
      inputStyles.value = {
        position: 'absolute',
        left: `${canvasX - 25}px`, // 可以考虑做一些微调
        top: `${canvasY + 18}px`, // 根据需要微调
        // 使输入框的光标对齐到点击位置
      };
      state.hoveredMarker!.labels = !state.hoveredMarker!.labels;
      draw('');
      state.hoveredMarker = null;
      return;
    }
    // 添加标记的可以屏蔽掉
    if (state.canBiaoZhu) {
      // 计算图像坐标
      const imgX =
        (canvasX -
          (state.canvas!.width / 2 - (state.imgWidth * state.imgScale) / 2)) /
        state.imgScale;
      const imgY =
        (canvasY -
          (state.canvas!.height / 2 - (state.imgHeight * state.imgScale) / 2)) /
        state.imgScale;

      createMarker(imgX, imgY);
    } else if (state.hoveredMarker) {
      state.currentMarker = state.hoveredMarker;
      state.currentText = state.hoveredMarker.text || '';
      markingPointsId.value = state.hoveredMarker.markingPointsId;
      // 判断当前是否是教学模式 如果是 则不处理查询点位详情和其他操作

      draw('搜索');

      // getxqPoint({ markPointsId: markingPointsId.value }).then((res: any) => {
      //   if (res.code === 200) {
      //     bwstuddata.value = res.data;
      //     console.log('查询点位详情完成', res.data);
      //     handleClick();
      //   }
      // });
    }
  };

  const bwstuddata = ref();
  const createMarker = (x: number, y: number) => {
    if (state.canBiaoZhu) {
      state.addflag = true;

      state.currentMarker = { x, y, color: state.pointColor };
      // draw('');
    }
  };

  const zoomIn = () => {
    state.imgScale += state.zoomStep;
    draw('');
  };

  const zoomOut = () => {
    if (state.imgScale > state.zoomStep) {
      state.imgScale -= state.zoomStep;
      draw('');
    }
  };

  const draw = (mbmarker: any) => {
    // console.log('当前缩放比例', state.imgScale);

    // console.log('来自哪里的重绘', mbmarker);

    const canvas = state.canvas!;
    const ctx = state.ctx!;
    const img = state.img;

    if (state.imgWidth == 0) {
      return;
    }

    // 清空画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    const scaledWidth = state.img.naturalWidth * state.imgScale;
    const scaledHeight = state.img.naturalHeight * state.imgScale;

    // 计算图像在画布上的偏移
    const xOffset = (canvas.width - scaledWidth) / 2 + state.imgOffsetX;
    const yOffset = (canvas.height - scaledHeight) / 2 + state.imgOffsetY;

    ctx.save();

    // 应用变换
    ctx.translate(canvas.width / 2, canvas.height / 2); // 移动到画布中心
    if (state.flipHorizontal) ctx.scale(-1, 1); // 水平翻转
    if (state.flipVertical) ctx.scale(1, -1); // 垂直翻转
    ctx.rotate((state.rotation * Math.PI) / 180); // 旋转
    ctx.translate(-canvas.width / 2, -canvas.height / 2); // 回到原位置

    // 绘制图像
    ctx.drawImage(img, xOffset, yOffset, scaledWidth, scaledHeight);

    // 恢复上下文状态为绘制标记做准备
    ctx.restore();
    ctx.save();

    // 重新应用变换
    ctx.translate(canvas.width / 2, canvas.height / 2);
    if (state.flipHorizontal) ctx.scale(-1, 1);
    if (state.flipVertical) ctx.scale(1, -1);
    ctx.rotate((state.rotation * Math.PI) / 180);
    ctx.translate(-canvas.width / 2, -canvas.height / 2);

    const margin = 200; // 从画布边缘的边距
    let textHeight = 16; // 文本高度
    let verticalSpacing = 5; // 文本之间的垂直间隔
    const textMargin = 20; // 文本与边缘的距离

    // 记录所有标记的位置和文本
    leftMarkers.value = [];
    rightMarkers.value = [];

    // 定义绘制标记和文本的函数
    const drawMarkersAndText = (markers: any[], isLeft: boolean) => {
      // console.log('继续执行');
      markers.forEach((item) => {
        if (!item.medicineAtlasImgId) {
          return;
        }
        const itemsArray = item.medicineAtlasImgId.split(',').map(Number);

        if (itemsArray.includes(dcomid.value)) {
          item.bsdata.forEach((marker: any) => {
            const markerX = marker.x * state.imgScale + xOffset;
            const markerY = marker.y * state.imgScale + yOffset;

            // 划分左右位置数组保存
            const count = leftMarkers.value.filter(
              (item: any) => item.flag == true
            ).length;
            const counts = rightMarkers.value.filter(
              (item: any) => item.flag == true
            ).length;

            // 根据位置将标记存入对应数组
            if (isLeft) {
              if (markerX < canvas.width / 2) {
                if (count > 23) {
                  leftMarkers.value.push(marker);
                } else {
                  marker.lintextshow = marker.flag;
                  leftMarkers.value.push(marker);
                }
              }
            } else {
              if (markerX >= canvas.width / 2) {
                if (counts > 23) {
                  marker.lintextshow = false;
                  rightMarkers.value.push(marker);
                } else {
                  marker.lintextshow = marker.flag;
                  rightMarkers.value.push(marker);
                }
              }
            }

            // 绘制标记点
            if (marker.flag) {
              if (marker.tack) {
                // 开启大头针
                // 开启大头针
                ctx.beginPath();

                // 定义大头针参数
                const baseSize = state.pointSize; // 使用 state.pointSize 作为基础尺寸
                const radius = baseSize * 1.5; // 顶部圆的半径（增大比例）
                const height = baseSize * 3; // 大头针的整体高度（增大比例）

                // 绘制顶部圆形
                ctx.arc(markerX, markerY, radius, Math.PI, 0, false); // 顺时针半圆

                // 绘制底部尖角
                ctx.lineTo(markerX, markerY + height); // 底部尖点
                ctx.lineTo(markerX - radius, markerY); // 左侧连接圆形

                // 完成路径
                ctx.closePath();

                // 设置样式并填充
                ctx.fillStyle =
                  marker == state.hoveredMarker
                    ? '#409EFF'
                    : '#' + marker.color;
                ctx.fill();

                // 如果需要边框
                ctx.lineWidth = 2;
                ctx.strokeStyle = 'black'; // 边框颜色
                ctx.stroke();
              } else {
                // 未开启大头针
                ctx.fillStyle =
                  marker == state.hoveredMarker
                    ? '#409EFF'
                    : '#' + marker.color;

                if (mbmarker == '搜索') {
                  ctx.fillStyle =
                    marker == state.hoveredMarker
                      ? 'rgba(255, 0, 0, 1)'
                      : 'rgba(128, 128, 128, 0.5)';
                }
                ctx.beginPath();
                ctx.arc(markerX, markerY, state.pointSize / 2, 0, 2 * Math.PI);
                ctx.fill();
              }
            }
          });
        }
      });
    };

    // 绘制左侧和右侧的标记
    drawMarkersAndText(state.markers, true); // 左侧标记
    drawMarkersAndText(state.markers, false); // 右侧标记

    // 左右数组优化调整顺序
    leftMarkers.value.sort((a: any, b: any) => a.y - b.y);
    rightMarkers.value.sort((a: any, b: any) => a.y - b.y);

    // 可用区域的总高度
    const availableHeight = canvas.height - 2 * textMargin;

    // 处理左侧或右侧数据
    const calculateStartYAndHeight = (markers: any) => {
      const markerCount = markers.length;

      if (markerCount === 0) {
        console.warn('无数据，返回默认值');
        return { totalTextHeight: 0, startY: textMargin };
      }

      // 计算总文本高度
      let totalTextHeight =
        markerCount * (textHeight + verticalSpacing) - verticalSpacing;

      // 动态调整文本大小和间距
      if (totalTextHeight < availableHeight / 2) {
        const scaleFactor = availableHeight / (2 * totalTextHeight);
        textHeight *= Math.min(scaleFactor, 1.4); // 限制最大放大倍数
        verticalSpacing *= Math.min(scaleFactor, 1.5);
        totalTextHeight =
          markerCount * (textHeight + verticalSpacing) - verticalSpacing;
      }

      // 计算起始位置
      const startY = Math.max(
        textMargin,
        (availableHeight - totalTextHeight) / 2 + textMargin
      );

      return { totalTextHeight, startY };
    };

    // 计算每个文本的位置
    const calculateTextPositions = (markers: any[], startY: number) => {
      let yPosition = startY;

      markers.forEach(
        (marker: { flag: any; lintextshow: boolean; textY: any }) => {
          if (marker.flag && marker.lintextshow) {
            // 检查是否超出可用高度
            if (yPosition + textHeight > canvas.height - textMargin) {
              marker.lintextshow = marker === mbmarker; // 仅显示特定标记
              if (marker.lintextshow) {
                marker.textY = markers[0].textY; // 默认与第一个文本对齐
              }
            } else {
              marker.textY = yPosition;
              marker.lintextshow = true;
              yPosition += textHeight + verticalSpacing;
            }
          }
        }
      );
    };

    // 分别计算左侧和右侧文本位置
    const { startY: leftStartY } = calculateStartYAndHeight(leftMarkers.value);
    const { startY: rightStartY } = calculateStartYAndHeight(
      rightMarkers.value
    );

    calculateTextPositions(leftMarkers.value, leftStartY);
    calculateTextPositions(rightMarkers.value, rightStartY);

    // 定义绘制文本和线条的函数
    const drawTextAndLines = (markers: any, isLeft: any) => {
      markers.forEach((marker: any, index: number) => {
        const { text, textY } = marker;
        const markerX = marker.x * state.imgScale + xOffset;
        const markerY = marker.y * state.imgScale + yOffset;
        const textX = isLeft ? margin : canvas.width - margin;

        if (
          marker.lintextshow &&
          marker.flag &&
          marker.labels &&
          !marker.tack
        ) {
          ctx.strokeStyle =
            marker == state.hoveredMarker ? '#409EFF' : '#' + marker.color;
          if (mbmarker == '搜索') {
            ctx.strokeStyle =
              marker == state.hoveredMarker
                ? 'rgba(255, 0, 0, 1)'
                : 'rgba(128, 128, 128, 0.5)';
          }

          ctx.lineWidth = 1.5;
          ctx.beginPath();
          ctx.moveTo(markerX, markerY);
          // 确定线条结束点
          const lineEndX = isLeft
            ? Math.max(textX, margin + 20)
            : Math.min(textX, canvas.width - margin - 20);
          ctx.lineTo(lineEndX, textY);
          ctx.stroke();
          // 判断左右
          if (isLeft) {
            // 1. 绘制水平直线：在连接线的末端绘制一条水平直线
            const horizontalLineLength = 10; // 水平直线的长度
            ctx.lineTo(lineEndX - horizontalLineLength, textY); // 向右绘制水平直线

            // 2. 绘制竖直线：从水平直线的末端绘制一条竖直线
            const verticalLineHeight = 5; // 竖直线的长度
            ctx.lineTo(
              lineEndX - horizontalLineLength,
              textY - 10 - verticalLineHeight
            ); // 绘制竖直线

            ctx.stroke();
          } else {
            // 1. 绘制水平直线：在连接线的末端绘制一条水平直线
            const horizontalLineLength = 10; // 水平直线的长度
            ctx.lineTo(lineEndX + horizontalLineLength, textY); // 向右绘制水平直线

            // 2. 绘制竖直线：从水平直线的末端绘制一条竖直线
            const verticalLineHeight = 5; // 竖直线的长度
            ctx.lineTo(
              lineEndX + horizontalLineLength,
              textY - 10 - verticalLineHeight
            ); // 绘制竖直线

            ctx.stroke();
          }

          // 绘制文本
          ctx.fillStyle =
            marker == state.hoveredMarker ? '#409EFF' : '#' + marker.color;
          if (mbmarker == '搜索') {
            ctx.fillStyle =
              marker == state.hoveredMarker
                ? 'rgba(255, 0, 0, 1)'
                : 'rgba(128, 128, 128, 0.5)';
          }

          ctx.textAlign = isLeft ? 'right' : 'left';
          ctx.font = `${textHeight}px Arial`;

          // 显示答案
          if (state.showText) {
            // 显示答案和用户答案
            if (marker.userText) {
              ctx.fillText(
                marker.text + ' / ' + marker.userText || '',
                textX,
                textY
              );
            } else {
              // 只显示答案
              ctx.fillText(text, textX, textY);
            }
          } else {
            // 只显示用户写
            ctx.fillText(marker.userText || index + 1, textX, textY);
          }
          // ctx.fillText(text, textX, textY);

          // 更新状态
          state.markers.forEach((item: any) => {
            if (!item.medicineAtlasImgId) {
              return;
            }
            const itemsArray = item.medicineAtlasImgId.split(',').map(Number);
            if (itemsArray.includes(dcomid.value)) {
              item.bsdata.forEach((markers: any) => {
                if (markers.markingPointsId == marker.markingPointsId) {
                  markers.textX = textX;
                  markers.textY = textY;
                }
              });
            }
          });
        }
      });
    };

    // 绘制左侧和右侧的文本和线条
    drawTextAndLines(leftMarkers.value, true);
    drawTextAndLines(rightMarkers.value, false);

    // 恢复上下文状态
    ctx.restore();
    loding.value = false;
  };

  // 优化后的绘制函数

  const gosroll = ref();
  const getScrollPosition = () => {
    // console.log('获取到当前为止', gosroll);
    if (gosroll.value) {
      // console.log('获取到当前为止', gosroll.value.scrollLeft);

      return gosroll.value.scrollLeft;
    }
  };
  // console.log('Current Scroll Position:', getScrollPosition());
  // 保存
  const saveMarkers = () => {
    // localStorage.setItem('markers', JSON.stringify(state.markers));
    // draw('');
  };

  const loadMarkers = () => {
    const cachedMarkers = localStorage.getItem('markers');
    if (cachedMarkers) {
      state.markers = JSON.parse(cachedMarkers);
      draw('');
    }
  };

  const toggleLinesAndText = () => {
    state.showLinesAndText = !state.showLinesAndText;
    draw('');
  };

  // 增加条件开始判断
  const tdflag = ref(false);

  const startDrag = (e: MouseEvent | TouchEvent) => {
    if (!tdflag.value) {
      return;
    }
    console.log('当前拖动标记');
    // 判断是否为触摸事件
    const isTouchEvent = (e: MouseEvent | TouchEvent): e is TouchEvent =>
      'touches' in e;

    let clientX: number, clientY: number;

    if (isTouchEvent(e)) {
      // 触摸事件
      clientX = e.touches[0].clientX;
      clientY = e.touches[0].clientY;
      e.preventDefault(); // 仅在触摸事件时调用 preventDefault
    } else {
      // 鼠标事件
      clientX = e.clientX;
      clientY = e.clientY;
      if (e.button === 0) {
        e.preventDefault(); // 仅在左键点击时调用 preventDefault
      }
    }

    // 判断是否为左键点击或触摸
    if ((e instanceof MouseEvent && e.button === 0) || isTouchEvent(e)) {
      if (tdflag.value) {
        state.isDragging = true;

        // 获取 canvas 的位置和尺寸
        const rect = state.canvas!.getBoundingClientRect();
        console.log('Canvas rect:', rect); // 输出 rect 检查是否精确

        // 确保在计算开始位置时能处理动态变化
        state.startDragX = clientX - rect.left - state.imgOffsetX;
        state.startDragY = clientY - rect.top - state.imgOffsetY;
      }
    }
  };

  const stopDrag = () => {
    state.isDragging = false;
    tdflag.value = false;
  };
  const dragMove = (e: MouseEvent | TouchEvent) => {
    // 判断是否是鼠标事件还是触摸事件
    const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
    const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;

    if (state.isDragging) {
      const rect = state.canvas!.getBoundingClientRect();
      state.imgOffsetX = clientX - rect.left - state.startDragX;
      state.imgOffsetY = clientY - rect.top - state.startDragY;
      draw('');
    }
  };

  const handleKeyDown = (e: KeyboardEvent) => {
    if (e.code === 'Space') {
      state.isSpacePressed = true;
    }
  };

  const handleKeyUp = (e: KeyboardEvent) => {
    if (e.code === 'Space') {
      state.isSpacePressed = false;
    }
  };
  const tagdcom = (i: number, bs: any) => {
    tackshow.value = false;
    state.imgWidth = 0;
    state.imgHeight = 0;
    const canvas = state.canvas!;
    const aspectRatio = state.img.naturalWidth / state.img.naturalHeight;
    const canvasAspectRatio = canvas.width / canvas.height;
    // 设置最大宽度和高度
    // const maxWidth = canvas.width * 0.999; // 画布宽度的99.9%
    // const maxHeight = canvas.height * 0.999; // 画布高度的99.9%

    // if (canvasAspectRatio > aspectRatio) {
    //   state.imgScale = Math.min(maxHeight / state.img.naturalHeight, 1);
    // } else {
    //   state.imgScale = Math.min(maxWidth / state.img.naturalWidth, 1);
    // }

    // 更新滚动位置和标志位
    getScrollPosition();
    flag.value = false;

    // 查找图片源并更新图片
    const imgsrcd = imgsrc.value.find((item) => item.medicineAtlasImgId === i);

    if (!imgsrcd) {
      return;
    }

    dcomid.value = i;

    // 切换图片的时候进行 更换相关解剖结构数组
    anatomicStructurefn();

    //  更换相关解剖结构数组 完成
    //本地缓存 历史记录
    localStorage.setItem('dcomid', String(dcomid.value));
    // 确保图片允许跨域
    state.img.crossOrigin = 'anonymous';
    state.img.src = base.value + imgsrcd.imgUrl;

    state.img.onload = () => {
      // 获取 Canvas 的绘图上下文
      if (!state.ctx) {
        return;
      }
      // 清空画布并绘制图片
      // const ctx = state.ctx;
      state.ctx.drawImage(state.img, 0, 0, canvas.width, canvas.height);
      // ctx.drawImage(state.img, xOffset, yOffset, scaledWidth, scaledHeight);
      // 更新图片宽高
      state.imgWidth = state.img.width;
      state.imgHeight = state.img.height;
      // 调用绘制函数

      // 判断当前是不是在教学模式
      const matchingItems = state.markers.filter((item: any) =>
        item.medicineAtlasImgId.includes(dcomid.value)
      );

      if (jubushow.value) {
        matchingItems.forEach((item) => {
          item.bsdata.forEach((items: any) => {
            items.labels = false; // 切换标签状态
          });
        });
      } else {
        matchingItems.forEach((item) => {
          item.bsdata.forEach((items: any) => {
            items.labels = true; // 切换标签状态
          });
        });
      }
      if (bs == '搜索') {
        draw('搜索');
      } else {
        draw('切换图片');
      }
    };
  };

  const optionsdata = ref([
    { anatomicStructureName: '', flag: true, anatomicStructureId: '' },
  ]);

  const tagdcoms = (i: any) => {
    // console.log('i', i);

    const index = imgsrc.value.findIndex(
      (item) => item.medicineAtlasImgId == dcomid.value
    );
    if (i == 1) {
      if (imgsrc.value[index + 1]) {
        dcomid.value = imgsrc.value[index + 1].medicineAtlasImgId;
        tagdcom(dcomid.value, '');
      }
    } else if (i == -1) {
      if (imgsrc.value[index - 1]) {
        dcomid.value = imgsrc.value[index - 1].medicineAtlasImgId;
        tagdcom(dcomid.value, '');
      }
    } else if (i == 10) {
      if (imgsrc.value[index + 10]) {
        dcomid.value = imgsrc.value[index + 10].medicineAtlasImgId;
        console.log('imgsrc.value[index + 10]', imgsrc.value[index + 10]);

        tagdcom(dcomid.value, '');
      }
    } else if (i == -10) {
      if (imgsrc.value[index - 10]) {
        dcomid.value = imgsrc.value[index - 10].medicineAtlasImgId;
        tagdcom(dcomid.value, '');
      }
    }
  };
  const findindex = () => {
    return imgsrc.value.findIndex(
      (item) => item.medicineAtlasImgId == dcomid.value
    );
  };
  const handleWheel = (e: WheelEvent) => {
    e.preventDefault();
    if (e.deltaY < 0 && dcomid.value > 1) {
      const index = imgsrc.value.findIndex(
        (item) => item.medicineAtlasImgId == dcomid.value
      );
      if (imgsrc.value[index - 1]) {
        dcomid.value = imgsrc.value[index - 1].medicineAtlasImgId;
        tagdcom(dcomid.value, '');
      }
    } else {
      const index = imgsrc.value.findIndex(
        (item) => item.medicineAtlasImgId == dcomid.value
      );
      if (imgsrc.value[index + 1]) {
        dcomid.value = imgsrc.value[index + 1].medicineAtlasImgId;
        tagdcom(dcomid.value, '');
      }
    }
  };
  const jiegoudata = ref([]);
  const box = ref();
  const oneshow = ref(false);
  let startX = 0; // 触摸开始时的 X 坐标
  let startY = 0; // 触摸开始时的 Y 坐标
  let isSwiping = false; // 判断是否正在滑动
  let startTime = 0; // 触摸开始时的时间
  let threshold = 30; // 滑动阈值，决定是否视为有效滑动
  let fastSwipeThreshold = 200; // 快速滑动的最小时间（单位：毫秒）
  let fastSwipeDistance = 50; // 快速滑动的最小位移（单位：像素）
  let slowSwipeThreshold = 10; // 慢速滑动的最大位移（单位：像素）
  let slowSwipeTimeThreshold = 200; // 慢速滑动的最大时间阈值（单位：毫秒）

  // 用于判断快速滑动的标记
  let isFastSwipe = false;
  let hasMoved = false; // 判断是否已经滑动过

  // 处理水平滑动
  const handleSwipeHorizontal = (diffX: any) => {
    if (!oneshow.value) {
      return;
    }

    if (jubushow.value) {
      return;
    }

    if (diffX > 0) {
      tagdcoms('1'); // 向右滑动
    } else {
      tagdcoms('-1'); // 向左滑动
    }
  };

  // 处理垂直滑动
  const handleSwipeVertical = (diffY: any) => {
    if (!oneshow.value) {
      return;
    }
    if (jubushow.value) {
      return;
    }

    if (diffY > 0) {
      tagdcoms('1'); // 向下滑动
    } else {
      tagdcoms('-1'); // 向上滑动
    }
  };

  // 注册滑动开始事件
  const touchStartHandler = (e: any) => {
    if (!oneshow.value) {
      return;
    }
    if (jubushow.value) {
      return;
    }

    const touch = e.touches[0];
    startX = touch.clientX;
    startY = touch.clientY;
    startTime = Date.now(); // 记录滑动开始的时间
    isSwiping = true; // 开始滑动
    hasMoved = false; // 重置滑动标记
    isFastSwipe = false; // 每次开始滑动时，重置快速滑动标记
  };

  // 注册滑动移动事件
  const touchMoveHandler = (e: any) => {
    if (!oneshow.value) {
      return;
    }
    if (jubushow.value) {
      return;
    }

    if (!isSwiping) return;

    const touch = e.touches[0];
    const diffX = touch.clientX - startX; // X 轴偏移量
    const diffY = touch.clientY - startY; // Y 轴偏移量
    const currentTime = Date.now();
    const elapsedTime = currentTime - startTime; // 计算滑动的时间（毫秒）

    // 判断滑动的方向和速度
    if (Math.abs(diffX) > threshold || Math.abs(diffY) > threshold) {
      // 判断是否为快速滑动：时间大于 fastSwipeThreshold 且位移大于 fastSwipeDistance
      isFastSwipe =
        elapsedTime >= fastSwipeThreshold &&
        (Math.abs(diffX) >= fastSwipeDistance ||
          Math.abs(diffY) >= fastSwipeDistance);

      if (isFastSwipe) {
        // 如果是快速滑动，则直接处理
        if (Math.abs(diffX) > Math.abs(diffY)) {
          handleSwipeHorizontal(diffX);
        } else {
          handleSwipeVertical(diffY);
        }
      } else {
        // 标记已经开始滑动
        hasMoved = true;
      }
    }
  };

  // 注册滑动结束事件
  const touchEndHandler = (e: any) => {
    if (!oneshow.value) {
      return;
    }
    const touch = e.changedTouches[0];
    const diffX = touch.clientX - startX; // X 轴偏移量
    const diffY = touch.clientY - startY; // Y 轴偏移量
    const currentTime = Date.now();
    const elapsedTime = currentTime - startTime; // 计算滑动的时间（毫秒）

    // 判断是否是慢速滑动
    if (!isFastSwipe) {
      // 慢速滑动：滑动距离小，时间较短
      if (
        elapsedTime <= slowSwipeTimeThreshold &&
        Math.abs(diffX) <= slowSwipeThreshold &&
        Math.abs(diffY) <= slowSwipeThreshold
      ) {
        // 触发慢速滑动
        if (Math.abs(diffX) > Math.abs(diffY)) {
          handleSwipeHorizontal(diffX);
        } else {
          handleSwipeVertical(diffY);
        }
      } else if (hasMoved) {
        // 如果有滑动且没有快速滑动的条件，则触发慢速滑动
        if (Math.abs(diffX) > Math.abs(diffY)) {
          handleSwipeHorizontal(diffX);
        } else {
          handleSwipeVertical(diffY);
        }
      }
    }

    // 重置滑动状态
    isSwiping = false;
    hasMoved = false;
  };

  // 处理图片滑动完成

  // 双指检测
  // 假设你已经定义了 state 和 draw 函数
  let initialDistance = 0; // 初始两指间距离
  let initialTouches: any[] = []; // 记录初始两个手指的位置
  const isZooming = ref(false); // 用于存储是否开始放大

  // 计算两点间的距离
  function getDistance(
    touch1: { clientX: number; clientY: number },
    touch2: { clientX: number; clientY: number }
  ) {
    const dx = touch2.clientX - touch1.clientX;
    const dy = touch2.clientY - touch1.clientY;

    return Math.sqrt(dx * dx + dy * dy);
  }

  // 触摸开始事件处理函数
  function handleTouchStart(e: any) {
    if (e.touches.length === 2 && isZooming.value) {
      // 记录两个手指的初始位置
      initialTouches = [e.touches[0], e.touches[1]];
      initialDistance = getDistance(initialTouches[0], initialTouches[1]);
    }
  }

  // 触摸移动事件处理函数
  function handleTouchMove(e: any) {
    if (e.touches.length === 2 && isZooming.value) {
      const currentDistance = getDistance(e.touches[0], e.touches[1]);
      const scaleChange = currentDistance / initialDistance;

      // 更新图片缩放
      state.imgScale *= scaleChange;
      draw('');

      // 更新初始距离为当前距离，以便下一次移动时计算缩放比例
      initialDistance = currentDistance;

      // 计算平移
      const deltaX =
        (e.touches[0].clientX + e.touches[1].clientX) / 2 -
        (initialTouches[0].clientX + initialTouches[1].clientX) / 2;
      const deltaY =
        (e.touches[0].clientY + e.touches[1].clientY) / 2 -
        (initialTouches[0].clientY + initialTouches[1].clientY) / 2;

      // 更新图片位置
      state.imgPosition = {
        x: state.imgPosition.x + deltaX,
        y: state.imgPosition.y + deltaY,
      };
      draw('');
    }
  }

  // 触摸结束事件处理函数
  function handleTouchEnd(e: any) {
    if (e.touches.length < 2) {
      initialDistance = 0;
      initialTouches = [];
    }
  }

  // 移除事件监听
  function removeTouchEvents() {
    // state.canvas.removeEventListener('touchstart', handleTouchStart, false);
    // state.canvas.removeEventListener('touchmove', handleTouchMove, false);
    // state.canvas.removeEventListener('touchend', handleTouchEnd, false);
  }

  // 双指检测完成
  onMounted(() => {
    loding.value = true;
    hbheight.value = box.value.offsetHeight;
    hbwidth.value = box.value.offsetWidth;
    state.canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
    state.ctx = state.canvas.getContext('2d', { willReadFrequently: true });
    state.img.crossOrigin = 'anonymous';
    // 处理图片id
    const imgsrcd = imgsrc.value.find(
      (item) => item.medicineAtlasImgId == dcomid.value
    );
    if (!imgsrcd) {
      state.img.src = base.value + imgsrc.value[0].imgUrl;
      dcomid.value = imgsrc.value[0].medicineAtlasImgId;
    } else {
      console.log('走了缓存');

      state.img.src = base.value + imgsrcd.imgUrl;
    }

    state.img.onload = () => {
      loadMarkers();

      state.imgWidth = state.img.width;
      state.imgHeight = state.img.height;
      // 查询标记点为
      getPointnew({
        medicineAtlasId: medicineAtlasId.value,
        pageNum: 1,
        pageSize: 800,
      }).then((res: any) => {
        if (res.code == 200) {
          state.markers = [];
          saveMarkers();
          // console.log('查询解刨点测试', res.data);
          const markersMap = new Map(
            state.markers.map((marker: any) => [
              marker.medicineAtlasImgId,
              marker,
            ])
          );

          for (const items of res.data) {
            try {
              if (items.medicineAtlasImgId != null) {
                const existingMarker = markersMap.get(items.medicineAtlasImgId);
                const markingPointsObjects = JSON.parse(items.markingPoints);
                markingPointsObjects.medicineAtlasImgId =
                  items.medicineAtlasImgId;

                markingPointsObjects.bsdata[0].markingPointsId =
                  items.markingPointsId;
                markingPointsObjects.bsdata[0].flag = true;
                markingPointsObjects.bsdata[0].labels = true;
                markingPointsObjects.bsdata[0].tack = false;
                markingPointsObjects.bsdata[0].anatomicStructureName =
                  items.anatomicStructureName;
                markingPointsObjects.bsdata[0].anatomicStructureId =
                  items.anatomicStructureId;

                if (existingMarker) {
                  existingMarker.bsdata.push(...markingPointsObjects.bsdata);
                } else {
                  markersMap.set(
                    items.medicineAtlasImgId,
                    markingPointsObjects
                  );
                }
              }
            } catch (error) {
              console.error('Error processing item:', items, error);
            }
          }

          // 更新 state.markers
          state.markers = Array.from(markersMap.values());

          // 确保数据不为空
          if (state.markers.length === 0) {
            console.error('Final markers array is empty. Check input data.');
          } else {
            // console.log('Final markers:', state.markers);
          }

          // console.log('打印获取的点位数据', state.markers);
          anatomicStructurefn();
          draw('');
        }
      });
    };
    state.img.onerror = (error) => {
      console.error('Failed to load image:', error);
    };
    state.canvas.addEventListener('mousedown', startDrag);
    state.canvas.addEventListener('mousemove', handleMouseMove);
    state.canvas.addEventListener('mouseup', stopDrag);
    state.canvas.addEventListener('touchmove', handleMouseMove);
    state.canvas.addEventListener('touchend', stopDrag);
    state.canvas.addEventListener('click', handleCanvasClick);
    state.canvas.addEventListener('wheel', handleWheel);
    state.canvas.addEventListener('touchstart', startDrag);
    state.canvas!.addEventListener('touchstart', handleCanvasClick, {
      passive: false,
    });

    // 添加单只滑动图片事件
    state.canvas.addEventListener('touchstart', touchStartHandler);
    state.canvas.addEventListener('touchmove', touchMoveHandler);
    state.canvas.addEventListener('touchend', touchEndHandler);
    state.canvas.addEventListener('touchcancel', touchEndHandler); // 取消触摸时也重置滑动状态

    // 添加双指事件
    state.canvas.addEventListener('touchstart', handleTouchStart, false);
    state.canvas.addEventListener('touchmove', handleTouchMove, false);
    state.canvas.addEventListener('touchend', handleTouchEnd, false);
    // 监听窗口大小变化
    window.addEventListener('resize', handleResize);

    document.addEventListener('keydown', handleKeyDown);
    document.addEventListener('keyup', handleKeyUp);

    // 获取一下解刨结构数据
    jpList({
      medicineAtlasId: medicineAtlasId.value,
      pageNum: 1,
      pageSize: 999,
    }).then((res: any) => {
      if (res.code == 200) {
        // 保存原有的数据 方便进行筛选
        optionsdata.value = res.data.list.map((item: any) => {
          return { ...item, flag: true }; // 为每个对象添加 flag 属性
        });

        // console.log('查询解刨数据成功', optionsdata.value);
        // 给结构添加属性来绑定
      }
    });
  });

  onBeforeUnmount(() => {
    if (state.canvas) {
      state.canvas.removeEventListener('mousedown', startDrag);
      state.canvas.removeEventListener('mousemove', handleMouseMove);
      state.canvas.removeEventListener('mouseup', stopDrag);
      state.canvas.removeEventListener('click', handleCanvasClick);
      state.canvas.removeEventListener('wheel', handleWheel);
      // state.canvas.removeEventListener('resize', handleResize);
    }
    window.removeEventListener('resize', handleResize);
    document.removeEventListener('keydown', handleKeyDown);
    document.removeEventListener('keyup', handleKeyUp);
  });
  const reset = () => {
    // 重置状态
    state.imgScale = 1;
    state.zoomStep = 0.1;
    state.rotation = 0;
    state.flipHorizontal = false;
    state.flipVertical = false;
    state.imgOffsetX = 0;
    state.imgOffsetY = 0;
    state.currentText = '';
    state.currentMarker = {};
    state.addflag = false;
    state.showLinesAndText = true;

    // 重新加载并绘制图像
    const imgsrcd = imgsrc.value.find(
      (item) => item.medicineAtlasImgId == dcomid.value
    );

    state.img.src = base.value + imgsrcd!.imgUrl;
    state.img.onload = () => {
      loadMarkers();
      draw('');
    };
  };
  const changecsd = () => {
    console.log('当前全选了');

    if (allflag.value == true) {
      state.markers.forEach((item) => {
        item.bsdata.forEach((items: any) => {
          items.flag = true;
        });
      });
      optionsdata.value.forEach((isd) => {
        isd.flag = true;
      });

      saveMarkers();
      draw('');
    }
    if (allflag.value == false) {
      state.markers.forEach((item) => {
        item.bsdata.forEach((items: any) => {
          items.flag = false;
        });
      });
      optionsdata.value.forEach((isd) => {
        isd.flag = false;
      });

      saveMarkers();
      draw('');
      // console.log('当前是否全选', allflag.value, state.markers);
    }
  };
  const changecs = (i: any) => {
    console.log('关联解剖结构测试', anatomicStructure.value, i);
    state.markers.some((item: any) => {
      // 只改变当前页面代码
      // const itemsArray = item.medicineAtlasImgId.split(',').map(Number);
      // if (itemsArray.includes(dcomid.value)) {

      // }
      item.bsdata.some((itemd: any) => {
        if (itemd.anatomicStructureId == i.anatomicStructureId) {
          itemd.flag = i.flag;
        }
      });
    });
    if (!i.flag) {
      allflag.value = false;
    }

    // 判断是否存在 flag 为 false 的项
    allflag.value = !anatomicStructure.value.some(
      (item) => item.flag === false
    );

    draw('');
  };

  const allflag = ref(true);
  const classificationName = ref('');
  const imggroupname = ref('');
  const fieldNames = ref({
    value: 'classificationName',
    label: 'classificationName',
  });
  const fieldNamess = ref({
    value: 'typeName',
    label: 'typeName',
  });

  // 图像解刨面更改
  const changeimgclassfn = (i: any) => {
    // console.log('点击了', i);
    // classificationName.value = i.classificationName;
    imgsrc.value = [];
    imgsrcs.value.forEach((item: any) => {
      if (classificationName.value == item.imgClassificationName) {
        imgsrc.value.push(item);
      }
    });
    if (i == '全部') {
      imgsrc.value = imgsrcs.value;
    }
    //
    dcomid.value = imgsrc.value[0].medicineAtlasImgId;
    tagdcom(dcomid.value, '');
  };

  // 图像分组更改
  const changeimggroupfn = (i: any) => {
    if (i == '全部') {
      imggroupname.value = '全部';
      imgsrc.value = imgsrcs.value;
      dcomid.value = imgsrc.value[0].medicineAtlasImgId;
      tagdcom(dcomid.value, '');
      return;
    }
    imggroupname.value = i;
    const item = imggroup.value.find(
      (img: any) => img.typeName == imggroupname.value
    );
    // 处理图像classificationName.value.includes(img.imgClassificationName)
    console.log('检测到图像分组更改', item, imggroup.value, i);
    const imgsrcsd = item as any;
    const imgsrcmw = imgsrc.value;
    imgsrc.value = [];
    console.log('切换图像debug', imgsrcsd);
    //  加一层判断吧
    if (classificationName.value != '') {
      imgsrcsd.typeImg.forEach((itemd: any) => {
        if (classificationName.value == itemd.imgClassificationName) {
          imgsrc.value.push(itemd);
          console.log('切换图像debug3', imgsrc.value, imgsrc.value.length);
        }
      });
    } else {
      imgsrc.value = imgsrcsd.typeImg;
      console.log('切换图像debug2', imgsrc.value, imgsrc.value.length);
    }

    if (imgsrc.value.length > 0) {
      dcomid.value = imgsrc.value[0].medicineAtlasImgId;
      tagdcom(dcomid.value, '');
    } else {
      // console.log('当前图像没有T2');
      imgsrc.value = imgsrcmw;
      Message.warning({
        content: '暂无图像!',
      });
    }
    // imgsrc.value = item.typeImg;
  };
  const flag = ref(false);
  // 定义新的解剖结构数组
  const anatomicStructure = ref([
    {
      anatomicStructureName: '',
      flag: false,
      anatomicStructureId: '',
    },
  ]);
  // 定义存储新的解剖函数
  const anatomicStructurefn = () => {
    anatomicStructure.value = [];
    optionsdata.value.forEach((items) => {
      state.markers.some((item: any) => {
        if (!item.medicineAtlasImgId) {
          return;
        }

        const itemsArray = item.medicineAtlasImgId.split(',').map(Number);
        if (itemsArray.includes(dcomid.value)) {
          item.bsdata.forEach((itemd: any) => {
            if (itemd.anatomicStructureId == items.anatomicStructureId) {
              // 先检查是否已存在
              const exists = anatomicStructure.value.some(
                (a) => a.anatomicStructureId === items.anatomicStructureId
              );

              if (!exists) {
                anatomicStructure.value.push(items);
              }
            }
          });
        }
        return false;
      });
    });
  };
  // const flagfn = (i: any) => {
  //   // console.log('当前id是否存在本图中', i.anatomicStructureId);
  //   return state.markers.some((item: any) => {
  //     if (!item.medicineAtlasImgId) {
  //       return;
  //     }

  //     const itemsArray = item.medicineAtlasImgId.split(',').map(Number);
  //     if (itemsArray.includes(dcomid.value)) {
  //       return item.bsdata.some(
  //         (itemd: any) => itemd.anatomicStructureId === i.anatomicStructureId
  //       );
  //     }
  //     return false;
  //   });
  // };

  // 定义画布高度
  const hbheight = ref();
  const hbwidth = ref();
  // 定义画布

  // 定义左右两侧数据
  const leftMarkers = ref<any[]>([]);
  const rightMarkers = ref<any[]>([]);
  const textModeEnabled = ref(false); // 控制是否启用添加文字模式
  const jubushow = ref(false);

  // 局部改教学模式
  const jubu = () => {
    // console.log('jubushow', jubushow.value);

    const matchingItems = state.markers.filter((item: any) =>
      item.medicineAtlasImgId.includes(dcomid.value)
    );

    matchingItems.forEach((item) => {
      item.bsdata.forEach((items: any) => {
        items.labels = !jubushow.value; // 切换标签状态
      });
    });

    saveMarkers();
    draw('');
  };
  // 大头针模式
  const tackshow = ref(false);
  const tackfn = () => {
    const matchingItems = state.markers.filter((item: any) =>
      item.medicineAtlasImgId.includes(dcomid.value)
    );

    matchingItems.forEach((item) => {
      item.bsdata.forEach((items: any) => {
        items.tack = tackshow.value; // 切换标签状态
      });
    });

    draw('');
  };

  // 局部标签适配
  // const jubu = () => {
  //   console.log('jubushow', jubushow.value);

  //   const matchingItems = state.markers.filter((item: any) =>
  //     item.medicineAtlasImgId.includes(dcomid.value)
  //   );

  //   matchingItems.forEach((item) => {
  //     item.bsdata.forEach((items: any) => {
  //       items.labels = false; // 切换标签状态
  //     });
  //   });

  //   saveMarkers();
  //   draw('');

  //   const canvasContainer = document.getElementById('canvasContainer');
  //   if (!canvasContainer) return; // 确保容器存在

  //   // 确保父容器是相对定位
  //   canvasContainer.style.position = 'relative';

  //   // 检查是否已有子画布，若存在则移除并退出
  //   const existingOverlayCanvas = canvasContainer.querySelector(
  //     'canvas[data-role="overlay"]'
  //   );
  //   if (existingOverlayCanvas) {
  //     canvasContainer.removeChild(existingOverlayCanvas);
  //     canvasContainer
  //       .querySelectorAll('div[data-role="resize-handle"]')
  //       .forEach((circle) => circle.remove());
  //     return; // 不再执行后续代码
  //   }

  //   // 创建并设置新的子 canvas
  //   const overlayCanvas = document.createElement('canvas');
  //   overlayCanvas.width = 80;
  //   overlayCanvas.height = 80;
  //   overlayCanvas.style.cssText = `
  //               border: 1px solid #ffffff;
  //               position: absolute;
  //               z-index: 10;
  //               left: 750px; /* 初始位置 */
  //               top: 350px; /* 初始位置 */
  //               cursor: move;
  //           `;
  //   overlayCanvas.setAttribute('data-role', 'overlay'); // 添加标识以便后续查找
  //   canvasContainer.appendChild(overlayCanvas);

  //   // 当前画布状态
  //   let currentState = {
  //     width: overlayCanvas.width,
  //     height: overlayCanvas.height,
  //     left: parseFloat(overlayCanvas.style.left),
  //     top: parseFloat(overlayCanvas.style.top),
  //   };

  //   // 是否在拖动模式
  //   let isDragging = false;
  //   let dragOffsetX = 0;
  //   let dragOffsetY = 0;

  //   // 判断是否为触摸设备
  //   const isTouchDevice =
  //     'ontouchstart' in window || navigator.maxTouchPoints > 0;

  //   // 画布拖动逻辑
  //   const startDragx = (e: any) => {
  //     e.preventDefault();
  //     isDragging = true;
  //     const clientX = isTouchDevice ? e.touches[0].clientX : e.clientX;
  //     const clientY = isTouchDevice ? e.touches[0].clientY : e.clientY;
  //     dragOffsetX = clientX - overlayCanvas.offsetLeft;
  //     dragOffsetY = clientY - overlayCanvas.offsetTop;

  //     const onMove = (moveEvent: any) => {
  //       if (!isDragging) return;
  //       const newX =
  //         (isTouchDevice ? moveEvent.touches[0].clientX : moveEvent.clientX) -
  //         dragOffsetX;
  //       const newY =
  //         (isTouchDevice ? moveEvent.touches[0].clientY : moveEvent.clientY) -
  //         dragOffsetY;
  //       currentState.left = newX;
  //       currentState.top = newY;

  //       overlayCanvas.style.left = `${newX}px`;
  //       overlayCanvas.style.top = `${newY}px`;

  //       updateControlPoints();
  //       updateMarkers(); // 更新标记点状态
  //     };

  //     const stopDragx = () => {
  //       isDragging = false;
  //       window.removeEventListener(
  //         isTouchDevice ? 'touchmove' : 'mousemove',
  //         onMove
  //       );
  //       window.removeEventListener(
  //         isTouchDevice ? 'touchend' : 'mouseup',
  //         stopDragx
  //       );
  //       draw(''); // 拖动完成后更新画布
  //     };

  //     window.addEventListener(
  //       isTouchDevice ? 'touchmove' : 'mousemove',
  //       onMove
  //     );
  //     window.addEventListener(
  //       isTouchDevice ? 'touchend' : 'mouseup',
  //       stopDragx
  //     );
  //   };

  //   overlayCanvas.addEventListener(
  //     isTouchDevice ? 'touchstart' : 'mousedown',
  //     startDragx
  //   );

  //   // 更新圆点位置
  //   const updateControlPoints = () => {
  //     const canvasRect = overlayCanvas.getBoundingClientRect();
  //     const containerRect = canvasContainer.getBoundingClientRect();

  //     const controlPoints = [
  //       {
  //         x: canvasRect.left - containerRect.left,
  //         y: canvasRect.top - containerRect.top,
  //       }, // 左上角
  //       {
  //         x: canvasRect.left - containerRect.left + canvasRect.width / 2,
  //         y: canvasRect.top - containerRect.top,
  //       }, // 顶边中心
  //       {
  //         x: canvasRect.right - containerRect.left,
  //         y: canvasRect.top - containerRect.top,
  //       }, // 右上角
  //       {
  //         x: canvasRect.left - containerRect.left,
  //         y: canvasRect.top - containerRect.top + canvasRect.height / 2,
  //       }, // 左边中心
  //       {
  //         x: canvasRect.right - containerRect.left,
  //         y: canvasRect.top - containerRect.top + canvasRect.height / 2,
  //       }, // 右边中心
  //       {
  //         x: canvasRect.left - containerRect.left,
  //         y: canvasRect.bottom - containerRect.top,
  //       }, // 左下角
  //       {
  //         x: canvasRect.left - containerRect.left + canvasRect.width / 2,
  //         y: canvasRect.bottom - containerRect.top,
  //       }, // 底边中心
  //       {
  //         x: canvasRect.right - containerRect.left,
  //         y: canvasRect.bottom - containerRect.top,
  //       }, // 右下角
  //     ];

  //     controlHandles.forEach((circle: any, index: any) => {
  //       circle.style.left = `${controlPoints[index].x - 5}px`;
  //       circle.style.top = `${controlPoints[index].y - 5}px`;
  //     });
  //   };

  //   // 更新标记点状态
  //   const updateMarkers = () => {
  //     const scaledWidth = state.img.naturalWidth * state.imgScale;
  //     const scaledHeight = state.img.naturalHeight * state.imgScale;

  //     const canvasOffsetX =
  //       (state.canvas.width - scaledWidth) / 2 + state.imgOffsetX;
  //     const canvasOffsetY =
  //       (state.canvas.height - scaledHeight) / 2 + state.imgOffsetY;

  //     const localCanvasLeft = currentState.left - canvasOffsetX;
  //     const localCanvasTop = currentState.top - canvasOffsetY;
  //     const localCanvasRight = localCanvasLeft + currentState.width;
  //     const localCanvasBottom = localCanvasTop + currentState.height;

  //     matchingItems.forEach((item) => {
  //       item.bsdata.forEach((marker: any) => {
  //         const scaledMarkerX = marker.x * state.imgScale;
  //         const scaledMarkerY = marker.y * state.imgScale;

  //         const isInside =
  //           scaledMarkerX >= localCanvasLeft &&
  //           scaledMarkerX <= localCanvasRight &&
  //           scaledMarkerY >= localCanvasTop &&
  //           scaledMarkerY <= localCanvasBottom;

  //         marker.labels = isInside; // 根据是否在区域内更新状态
  //       });
  //     });
  //   };

  //   // 添加圆点
  //   const controlHandles: any = [];
  //   const createCircle = (index: any) => {
  //     const circle = document.createElement('div');
  //     circle.style.cssText = `
  //                   width: 10px;
  //                   height: 10px;
  //                   background-color: #ff0000;
  //                   border-radius: 50%;
  //                   position: absolute;
  //                   z-index: 15;
  //                   cursor: pointer;
  //               `;
  //     circle.setAttribute('data-index', index);
  //     circle.setAttribute('data-role', 'resize-handle');
  //     canvasContainer.appendChild(circle);
  //     return circle;
  //   };

  //   for (let i = 0; i < 8; i++) {
  //     const circle = createCircle(i);
  //     controlHandles.push(circle);
  //     let isResizing = false;

  //     circle.addEventListener(
  //       isTouchDevice ? 'touchstart' : 'mousedown',
  //       (e: any) => {
  //         e.preventDefault();
  //         e.stopPropagation(); // 防止触发拖动事件
  //         isResizing = true;

  //         const startX = isTouchDevice ? e.touches[0].clientX : e.clientX;
  //         const startY = isTouchDevice ? e.touches[0].clientY : e.clientY;

  //         const startState = { ...currentState };

  //         const onResizeMove = (moveEvent: any) => {
  //           if (!isResizing) return;

  //           const dx =
  //             (isTouchDevice
  //               ? moveEvent.touches[0].clientX
  //               : moveEvent.clientX) - startX;
  //           const dy =
  //             (isTouchDevice
  //               ? moveEvent.touches[0].clientY
  //               : moveEvent.clientY) - startY;

  //           switch (i) {
  //             case 0: // 左上角
  //               currentState.width = startState.width - dx;
  //               currentState.height = startState.height - dy;
  //               currentState.left = startState.left + dx;
  //               currentState.top = startState.top + dy;
  //               break;
  //             case 1: // 顶边中心
  //               currentState.height = startState.height - dy;
  //               currentState.top = startState.top + dy;
  //               break;
  //             case 2: // 右上角
  //               currentState.width = startState.width + dx;
  //               currentState.height = startState.height - dy;
  //               currentState.top = startState.top + dy;
  //               break;
  //             case 3: // 左边中心
  //               currentState.width = startState.width - dx;
  //               currentState.left = startState.left + dx;
  //               break;
  //             case 4: // 右边中心
  //               currentState.width = startState.width + dx;
  //               break;
  //             case 5: // 左下角
  //               currentState.width = startState.width - dx;
  //               currentState.height = startState.height + dy;
  //               currentState.left = startState.left + dx;
  //               break;
  //             case 6: // 底边中心
  //               currentState.height = startState.height + dy;
  //               break;
  //             case 7: // 右下角
  //               currentState.width = startState.width + dx;
  //               currentState.height = startState.height + dy;
  //               break;
  //           }

  //           overlayCanvas.style.width = `${currentState.width}px`;
  //           overlayCanvas.style.height = `${currentState.height}px`;
  //           overlayCanvas.style.left = `${currentState.left}px`;
  //           overlayCanvas.style.top = `${currentState.top}px`;

  //           updateControlPoints();
  //           updateMarkers();
  //         };

  //         const stopResize = () => {
  //           isResizing = false;
  //           window.removeEventListener(
  //             isTouchDevice ? 'touchmove' : 'mousemove',
  //             onResizeMove
  //           );
  //           window.removeEventListener(
  //             isTouchDevice ? 'touchend' : 'mouseup',
  //             stopResize
  //           );
  //           draw(''); // 调整完成后更新画布
  //         };

  //         window.addEventListener(
  //           isTouchDevice ? 'touchmove' : 'mousemove',
  //           onResizeMove
  //         );
  //         window.addEventListener(
  //           isTouchDevice ? 'touchend' : 'mouseup',
  //           stopResize
  //         );
  //       }
  //     );
  //   }

  //   updateControlPoints(); // 初始化控制点位置
  //   updateMarkers(); // 初始化标记点状态
  // };

  // 局部标签完成
  // 添加画布 生成画板操作
  const pzshow = ref(false);
  const myStyle = ref('inherit');
  const context = ref<CanvasRenderingContext2D | null>(null);
  const overlayContext = ref<CanvasRenderingContext2D | null>(null); // 声明类型
  const isDrawing = ref(false);
  const isCanvasVisible = ref(false); // 用于控制画布是否可见
  const customText = ref(''); // 用于存储自定义输入的文字
  const showInput = ref(false); // 控制输入框是否显示

  const inputStyle = ref<CSSProperties>({
    left: '',
    top: '',
    position: 'absolute', // 'absolute' 是一个有效的 position 类型
  }); // 输入框的位置样式
  const isInputActive = ref(false); // 控制输入框是否激活
  const currentMode = ref('drawing'); // 初始为绘制模式
  const currentCursorStyle = ref('crosshair'); // 默认光标为十字架
  const pzfn = () => {
    // 动态显示画布并获取上下文
    isCanvasVisible.value = pzshow.value;

    nextTick(() => {
      const myCanvas = document.getElementById('myCanvas') as HTMLCanvasElement;
      const overlayCanvas = document.getElementById(
        'overlayCanvas'
      ) as HTMLCanvasElement;

      // 获取画布上下文
      context.value = myCanvas.getContext('2d', {
        willReadFrequently: true,
      }) as CanvasRenderingContext2D;
      overlayContext.value = overlayCanvas.getContext('2d', {
        willReadFrequently: true,
      }) as CanvasRenderingContext2D;

      // 保存初始状态（确保正确的画布）
      canvasHistory.value.push(
        overlayContext.value.getImageData(
          0,
          0,
          overlayCanvas.width,
          overlayCanvas.height
        )
      );

      console.log('初始状态已保存:', canvasHistory.value);
    });
  };

  const startDrawing = (event: any) => {
    if (currentMode.value !== 'drawing' && !isEraserMode.value) {
      return;
    }
    isDrawing.value = true;
    draws(event); // 开始绘制或擦除
  };

  const stopDrawing = () => {
    isDrawing.value = false;
    overlayContext.value!.beginPath();

    const MAX_HISTORY = 20; // 最大历史记录长度

    const addHistoryState = () => {
      const canvas = document.getElementById(
        'overlayCanvas'
      ) as HTMLCanvasElement;
      const currentState = overlayContext.value!.getImageData(
        0,
        0,
        canvas.width,
        canvas.height
      );

      // 如果超出最大长度，移除最早的记录
      if (canvasHistory.value.length >= MAX_HISTORY) {
        canvasHistory.value.shift();
      }
      canvasHistory.value.push(currentState);

      console.log('添加历史记录，当前记录数:', canvasHistory.value.length);
    };
    // 保存当前状态到历史记录
    addHistoryState();
  };

  const brushColor = ref('#ff0000'); // 默认画笔颜色
  const brushSize = ref(2); // 默认画笔粗细
  type HistoryItem = any; // 你可以用更具体的类型代替 `any`

  const canvasHistory = ref<HistoryItem[]>([]);
  const isEraserMode = ref(false); // 标识是否为橡皮擦模式

  // 绘制标记主函数
  const draws = (event: any) => {
    event.preventDefault();
    if (!isDrawing.value || !overlayContext.value) return;

    const rect = document
      .getElementById('overlayCanvas')
      ?.getBoundingClientRect();

    let x, y;

    // 判断是否是触摸事件
    if (event.touches && event.touches.length > 0) {
      x = event.touches[0].clientX - rect!.left;
      y = event.touches[0].clientY - rect!.top;
    } else {
      x = event.clientX - rect!.left;
      y = event.clientY - rect!.top;
    }

    if (isEraserMode.value) {
      // 如果是橡皮擦模式，擦除线段
      erase(x, y);
    } else {
      // 否则是绘制模式
      overlayContext.value.lineWidth = brushSize.value; // 使用动态线宽
      overlayContext.value.lineCap = 'round';
      overlayContext.value.strokeStyle = brushColor.value; // 使用动态颜色

      overlayContext.value.lineTo(x, y);
      overlayContext.value.stroke();
      overlayContext.value.beginPath();
      overlayContext.value.moveTo(x, y);
    }
  };

  // 切换橡皮擦模式
  const toggleEraser = () => {
    isEraserMode.value = !isEraserMode.value;
    console.log(isEraserMode.value ? '橡皮擦模式启用' : '橡皮擦模式禁用');
  };

  // 橡皮擦擦除函数
  const erase = (x: number, y: number, tolerance: number = 10) => {
    const canvas = document.getElementById(
      'overlayCanvas'
    ) as HTMLCanvasElement;

    // 获取画布数据
    const imageData = overlayContext.value!.getImageData(
      0,
      0,
      canvas.width,
      canvas.height
    );
    const data = imageData.data;

    // 获取擦除区域的像素数据
    const eraseRadius = brushSize.value + tolerance; // 扩大半径以确保擦除范围足够

    // 循环遍历每个像素
    for (let i = 0; i < data.length; i += 4) {
      const px = (i / 4) % canvas.width;
      const py = Math.floor(i / 4 / canvas.width);
      const dx = px - x;
      const dy = py - y;

      // 计算当前像素点到鼠标点的距离
      if (Math.sqrt(dx * dx + dy * dy) < eraseRadius) {
        data[i] = 255; // R
        data[i + 1] = 255; // G
        data[i + 2] = 255; // B
        data[i + 3] = 0; // A (透明度)
      }
    }

    // 更新画布
    overlayContext.value!.putImageData(imageData, 0, 0);
  };

  // 撤销
  const redrawCanvas = () => {
    const canvas = document.getElementById(
      'overlayCanvas'
    ) as HTMLCanvasElement | null;
    if (canvas) {
      overlayContext.value!.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
    }

    if (canvasHistory.value.length > 0) {
      // 恢复最后的画布状态
      const lastState = canvasHistory.value[canvasHistory.value.length - 1];
      overlayContext.value!.putImageData(lastState, 0, 0);
    }
  };

  const redoHistory = ref<HistoryItem[]>([]); // 保存还原的历史记录
  // 撤销方法
  const undo = () => {
    console.log('历史记录是否有', canvasHistory.value);
    if (canvasHistory.value.length > 0) {
      console.log('执行了撤销');
      // 保存当前状态到还原栈
      redoHistory.value.push(
        canvasHistory.value[canvasHistory.value.length - 1]
      );
      // const canvasHistoryd = canvasHistory.value;
      // redoHistory.value = canvasHistoryd;
      console.log('当前数据站', redoHistory.value);
      // 弹出最近的状态
      canvasHistory.value.pop();
      // 重绘画布
      nextTick(() => {
        redrawCanvas();
      });
    } else {
      clearOverlay();
      console.log('没有可以撤销的历史记录');
    }
  };

  // 还原方法
  const redo = () => {
    console.log('还原历史记录是否有', redoHistory.value);

    if (redoHistory.value.length > 0) {
      console.log('执行了还原');
      // 获取最近的还原状态
      const lastUndoState = redoHistory.value.pop();

      // 将其恢复到撤销栈
      canvasHistory.value.push(lastUndoState);

      // 重绘画布
      nextTick(() => {
        redrawCanvas();
      });
    } else {
      console.log('没有可以还原的历史记录');
    }
  };

  const showTextInput = (event: any) => {
    if (currentMode.value != 'text') {
      return;
    }

    const rect = document
      .getElementById('overlayCanvas')
      ?.getBoundingClientRect();
    const x = event.clientX - rect!.left;
    const y = event.clientY - rect!.top;

    // 设置输入框的位置
    inputStyle.value = {
      position: 'absolute',
      left: `${event.clientX - 10}px`, // 可以考虑做一些微调
      top: `${event.clientY - 65}px`, // 根据需要微调
      // 使输入框的光标对齐到点击位置
    };
    customText.value = ''; // 清空输入框内容
    showInput.value = true; // 显示输入框

    // 使用 nextTick 确保输入框显示后立即获得焦点
    nextTick(() => {
      const inputElement = document.querySelector(
        '.text-input'
      ) as HTMLInputElement | null;
      if (inputElement) {
        inputElement.focus(); // 设置焦点
      }
    });
  };

  const addTextMarker = (text: any, x: any, y: any) => {
    if (!overlayContext.value) {
      return; // 如果 overlayContext 未初始化，直接返回
    }

    overlayContext.value.font = '20px Arial'; // 设置字体和大小
    overlayContext.value.fillStyle = 'red'; // 设置文字颜色
    overlayContext.value.fillText(text, x, y); // 添加文字标记
    console.log('成功添加文字标记', text, x, y);
  };

  // 处理输入框失去焦点事件
  const addTextMarkerOnBlur = () => {
    if (customText.value) {
      console.log('输入框执行');

      addTextMarker(
        customText.value,
        parseFloat(inputStyle.value.left as string),
        parseFloat(inputStyle.value.top as string)
      ); // 获取输入框位置
    }
    showInput.value = false; // 隐藏输入框
  };

  // 处理回车事件
  const addTextMarkerOnEnter = (event: any) => {
    if (event.key === 'Enter' && customText.value) {
      console.log('输入框执行2');
      addTextMarker(
        customText.value,
        parseFloat(inputStyle.value.left as string),
        parseFloat(inputStyle.value.top as string)
      ); // 获取输入框位置
      showInput.value = false; // 隐藏输入框
    }
  };

  // 处理输入框失去焦点事件
  const handleInputBlur = () => {
    isInputActive.value = false; // 重置输入框激活状态
    addTextMarkerOnBlur();
  };

  // 切换到文字输入模式
  const enableTextMode = () => {
    nextTick(() => {
      currentMode.value = 'text';
      currentCursorStyle.value = 'text';
    });
    // 设置为文本光标
  };

  // 切换到绘制模式
  const enableDrawingMode = () => {
    nextTick(() => {
      currentMode.value = 'drawing';
      currentCursorStyle.value = 'crosshair'; // 设置为十字架光标
    });
  };

  // 切换到保存图片下载模式
  const downloadImage = () => {
    currentMode.value = 'download';
    const myCanvas = document.getElementById('myCanvas') as HTMLCanvasElement;
    const overlayCanvas = document.getElementById(
      'overlayCanvas'
    ) as HTMLCanvasElement;

    const combinedCanvas = document.createElement('canvas');
    combinedCanvas.width = hbwidth.value;
    combinedCanvas.height = hbheight.value;
    const ctx = combinedCanvas.getContext('2d', {
      willReadFrequently: true,
    }) as CanvasRenderingContext2D;
    // 设置背景颜色为黑色
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, combinedCanvas.width, combinedCanvas.height);
    // 绘制原始画布的内容
    ctx.drawImage(myCanvas, 0, 0);

    // 绘制标记画布的内容
    ctx.drawImage(overlayCanvas, 0, 0);
    console.log('报错的拖', combinedCanvas);

    try {
      // 将合成的画布转换为数据URL
      const imageDataURL = combinedCanvas.toDataURL('image/png');

      // 创建下载链接并触发下载
      const link = document.createElement('a');
      link.href = imageDataURL;
      link.download = 'combined_image.png'; // 下载文件名
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    } catch (error) {
      console.error('Error exporting canvas: ', error);
    }
  };

  // 清除模式
  const clearOverlay = () => {
    currentMode.value = 'delel';
    const overlayCanvas = document.getElementById(
      'overlayCanvas'
    ) as HTMLCanvasElement;
    if (overlayCanvas) {
      const overlayContext = overlayCanvas.getContext('2d', {
        willReadFrequently: true,
      }) as CanvasRenderingContext2D;
      overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); // 清空画布
    } else {
      console.error('Overlay canvas not found');
    }
  };

  // 处理窗口大小变化的函数
  const handleResize = () => {
    // console.log('当前是否边伯贤', box.value);
    if (state.canvas && !isCanvasVisible.value) {
      // 更新画布的宽高

      hbheight.value = box.value.offsetHeight;
      hbwidth.value = box.value.offsetWidth;

      // 更新画布大小
      // 重新绘制画布内容（如果需要）
      state.canvas.width = hbwidth.value;
      state.canvas.height = hbheight.value;
      // state.imgWidth = state.img.width;
      // state.imgHeight = state.img.height;
      // 更新绘制
      tagdcom(dcomid.value, '');
    }
  };
</script>

<style scoped>
  .overlayCanvastooitems {
    background: #409eff;
  }
  .text-input-container {
    /* 输入框的容器样式 */
    z-index: 99;
  }

  .text-input {
    padding: 5px;
    border: 1px solid #ccc;
  }
  #overlayCanvas {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2; /* 画板层 */
  }
  .bjbox {
    width: 100%;
    height: 172px;
  }
  .bjbox img {
    width: 100%;
    height: 172px;
  }
  .mainboxgj {
    display: flex;
    width: 100%;
    height: 100%;
  }
  .mainlefttopbox {
    width: 100%;
    height: 6%;
    background-color: #24292d;

    display: flex;
    justify-content: center;
    align-items: center;
    overflow: hidden;
    position: relative;
    border-bottom: 1px solid #5a5a5a;
  }
  .mainsera {
    position: absolute;
    left: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .mainsers {
    position: absolute;
    left: 150px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .maintoptitle {
    font-weight: bold;
    font-size: 20px;
    color: #ffffff;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    justify-content: center;
    align-items: center;
    height: 54px;
  }
  .maintopright {
    display: flex;
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translate(0, -50%);
  }
  .czmainitem {
    cursor: pointer;
    margin: 0 5px;
  }
  .gnbocdtop .arco-btn {
    margin: 0;
    /* margin: 5px 10px; */
  }
  .gnbocdtop {
    width: 100%;
    height: 15%;
    background-color: black;
    border-bottom: 2px solid #5a5a5a;
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    overflow-y: auto;
    padding: 10px;
  }
  .gnbocd {
    width: 100%;
    height: 75%;
    overflow: hidden;
    overflow-y: auto;
    background-color: black;
    display: flex;
    flex-direction: column;
  }
  .gnbocdname {
    font-size: 20px;
    color: #ffffff;
    padding: 10px;
  }
  .gnbottom {
    width: 100%;
    background-color: #24292d;
    height: 10%;
    padding: 13px 0;
    z-index: 9;
    display: flex;
    align-items: center;
    justify-content: center;
    border-top: 2px solid #5a5a5a;
  }
  #canvasContainer {
    position: relative;
  }
  #myCanvas {
    /* border: solid 1px #00ff80; */
    background-color: black;
  }
  .mentox {
    width: 200px;
    height: 50px;
    background: #121419;
    border-radius: 6px 6px 6px 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 500;
    font-size: 18px;
    color: #ffffff;
    margin: 0 13px;
  }
  .mentox span {
    display: block;
    font-weight: bold;
    font-size: 24px;
    margin-left: 10px;
    word-spacing: 20px;
  }
  .boxs {
    width: 300px;
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
  }
  .zsdkz {
    font-size: 18px;
    color: #ffffff;
    line-height: 30px;
    padding: 5px 10px;
  }
  .boxsitem {
    width: 130px;
    height: 130px;
    margin: 10px 25px;
    background: black;
  }
  .boxsitems {
    text-align: center;
  }
  .box {
    margin: 0 auto;
    background-color: black;
    width: 100%;
    height: 85%;
    position: relative;
  }

  .bocd {
    /* margin: 10px; */
    width: 20%;
    height: 100%;
    display: flex;
    flex-direction: column;
    border-left: 2px solid #363739;
  }
  .mainboxleft {
    width: 80%;
    height: 100%;
  }
  .lbox {
    width: 100%;
    height: 10%;
    display: flex;
    overflow: hidden;
    overflow-x: auto;
    white-space: nowrap;

    /* border-top: 2px solid #5a5a5a; */
  }

  .lboxitem {
    background-color: black;
    width: 70px;
    /* height: 70px; */
    cursor: pointer;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .lboxitem img {
    width: 55px;
    height: 55px;
  }

  .msx {
    height: 100px;
    width: 2px;
    background-color: #409eff;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
  .gnbocd::-webkit-scrollbar {
    width: 5px;
    height: 5px;
  }

  .gnbocd::-webkit-scrollbar-track {
    width: 6px;
    background: rgba(#101f1c, 0.1);
    -webkit-border-radius: 2em;
    -moz-border-radius: 2em;
    border-radius: 2em;
  }

  .gnbocd::-webkit-scrollbar-thumb {
    background-color: rgba(133, 138, 148, 0.938);
    background-clip: padding-box;
    min-height: 28px;
    -webkit-border-radius: 2em;
    -moz-border-radius: 2em;
    border-radius: 2em;
    transition: background-color 0.3s;
    cursor: pointer;
  }

  .gnbocd::-webkit-scrollbar-thumb:hover {
    background-color: rgba(144, 147, 153, 0.3);
  }
  .lbox::-webkit-scrollbar {
    width: 5px;
    height: 5px;
  }

  .lbox::-webkit-scrollbar-track {
    width: 6px;
    background: rgba(#101f1c, 0.1);
    -webkit-border-radius: 2em;
    -moz-border-radius: 2em;
    border-radius: 2em;
  }

  .lbox::-webkit-scrollbar-thumb {
    background-color: rgba(133, 138, 148, 0.938);
    background-clip: padding-box;
    min-height: 28px;
    -webkit-border-radius: 2em;
    -moz-border-radius: 2em;
    border-radius: 2em;
    transition: background-color 0.3s;
    cursor: pointer;
  }

  .lbox::-webkit-scrollbar-thumb:hover {
    background-color: rgba(144, 147, 153, 0.3);
  }
  .jgdovx {
    margin: 5px 0;
    display: flex;
    justify-content: space-between;
    width: 100%;
  }
  .xugn {
    display: flex;
    /* justify-content: center; */
    align-items: center;
    margin: 5px 0;
    flex-wrap: wrap;
  }
  .xugname {
    width: 80px;
    color: #fff;
    font-size: 16px;
  }
  .xbanner {
    width: 100%;
    font-size: 16px;
    height: 40px;
    background-color: #fff;
    /* line-height: 40px; */
    padding-left: 10px;
    align-content: center;
  }
  .xbannername {
    cursor: pointer;
    display: flex;
    align-items: center;
    font-size: 16px;
  }
  /* 公共类 */
  /* 封装公共按钮类 */
  .tool {
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .tool img.active {
    cursor: pointer;
    display: none;
  }

  .tool:hover img.normal,
  .tool.active img.normal {
    display: none;
  }

  .tool:hover img.active,
  .tool.active img.active {
    display: block;
  }
  .gnboxmain {
    padding: 0 15px;
  }
  .gnboxitem {
    height: 40px;
    background: #24292d;
    border-radius: 4px 4px 4px 4px;
    display: flex;
    align-items: center;
    color: #fff;
    padding: 0 12px;
    margin: 10px 0;
    font-size: 16px;
  }
  .zsbox {
    width: 20%;
    height: 910px;
    background-color: #121419;
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0px;
    right: 0px;
    z-index: 9;
  }
  .zstop {
    height: 54px;
    width: 100%;
    font-size: 16px;
    color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  /* .arco-drawer-header {
    background-color: black !important;
  }
  .chouti .arco-drawer {
    background-color: black !important;
  } */
  .zsclose {
    position: absolute;
    right: 20px;
    cursor: pointer;
  }
  .xbannername {
    cursor: pointer;
    display: flex;
    align-items: center;
    color: #fff;
  }
  .jpan {
    background-color: #424548;
    border-radius: 4px;
    /* padding: 10px 28px; */
    cursor: pointer;
    width: 108px;
    height: 38px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 5px 7px;
    text-align: center;
  }
  .jpanitem {
    width: 70%;
    color: #fff;
    text-wrap: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .jpan:hover {
    background-color: #409eff;
    border-radius: 4px;
    max-width: 108px;
  }
  .jpanac {
    background-color: #409eff;
    border-radius: 4px;
    max-width: 108px;
  }
  .overlayCanvastool {
    position: absolute;
    top: 10px;
    right: 100px;
    z-index: 999;
    width: 600px;
    height: 70px;
    background-color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .overlayCanvastooitem {
    width: 50px;
    font-size: 30px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .startbox {
    width: 90px;
    height: 37px;
    background: linear-gradient(180deg, #f4f4f4 0%, #a2a2a2 100%);
    border-radius: 4px 4px 4px 4px;
    border: 1px solid #000000;
    font-weight: bold;
    font-size: 16px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 5px;
  }
  .startboxs {
    color: #fff;
    font-size: 18px;
  }
  .speech-bubble {
    position: absolute;
    background-color: rgba(165, 163, 163, 0.945);
    color: #fff;
    padding: 5px 10px;
    border-radius: 15px;
    width: fit-content;
    font-size: 14px;
  }

  /* 气泡的三角形 */
  .speech-bubble::after {
    content: '';
    position: absolute;
    bottom: -18px; /* 调整三角形位置 */
    left: 20px; /* 根据需要调整位置 */
    border-width: 10px;
    border-style: solid;
    border-color: rgba(165, 163, 163, 0.945) transparent transparent transparent; /* 与背景色一致 */
  }
</style>
